home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 July / maximum-cd-2011-07.iso / DiscContents / audacity-win-unicode-1.3.13.exe / {app} / help / manual / m / skins / common / wikibits.js / 207
Encoding:
Text File  |  2011-04-06  |  29.6 KB  |  1,001 lines

  1. // MediaWiki JavaScript support functions
  2.  
  3. var clientPC = navigator.userAgent.toLowerCase(); // Get client info
  4. var is_gecko = /gecko/.test( clientPC ) &&
  5.     !/khtml|spoofer|netscape\/7\.0/.test(clientPC);
  6. var webkit_match = clientPC.match(/applewebkit\/(\d+)/);
  7. if (webkit_match) {
  8.     var is_safari = clientPC.indexOf('applewebkit') != -1 &&
  9.         clientPC.indexOf('spoofer') == -1;
  10.     var is_safari_win = is_safari && clientPC.indexOf('windows') != -1;
  11.     var webkit_version = parseInt(webkit_match[1]);
  12. }
  13. var is_khtml = navigator.vendor == 'KDE' ||
  14.     ( document.childNodes && !document.all && !navigator.taintEnabled );
  15. // For accesskeys; note that FF3+ is included here!
  16. var is_ff2 = /firefox\/[2-9]|minefield\/3/.test( clientPC );
  17. var is_ff2_ = /firefox\/2/.test( clientPC );
  18. // These aren't used here, but some custom scripts rely on them
  19. var is_ff2_win = is_ff2 && clientPC.indexOf('windows') != -1;
  20. var is_ff2_x11 = is_ff2 && clientPC.indexOf('x11') != -1;
  21. if (clientPC.indexOf('opera') != -1) {
  22.     var is_opera = true;
  23.     var is_opera_preseven = window.opera && !document.childNodes;
  24.     var is_opera_seven = window.opera && document.childNodes;
  25.     var is_opera_95 = /opera\/(9.[5-9]|[1-9][0-9])/.test( clientPC );
  26. }
  27.  
  28. // Global external objects used by this script.
  29. /*extern ta, stylepath, skin */
  30.  
  31. // add any onload functions in this hook (please don't hard-code any events in the xhtml source)
  32. var doneOnloadHook;
  33.  
  34. if (!window.onloadFuncts) {
  35.     var onloadFuncts = [];
  36. }
  37.  
  38. function addOnloadHook(hookFunct) {
  39.     // Allows add-on scripts to add onload functions
  40.     if(!doneOnloadHook) {
  41.         onloadFuncts[onloadFuncts.length] = hookFunct;
  42.     } else {
  43.         hookFunct();  // bug in MSIE script loading
  44.     }
  45. }
  46.  
  47. function hookEvent(hookName, hookFunct) {
  48.     addHandler(window, hookName, hookFunct);
  49. }
  50.  
  51. function importScript(page) {
  52.     var uri = wgScript + '?title=' +
  53.         encodeURIComponent(page.replace(/ /g,'_')).replace('%2F','/').replace('%3A',':') +
  54.         '&action=raw&ctype=text/javascript';
  55.     return importScriptURI(uri);
  56. }
  57.  
  58. var loadedScripts = {}; // included-scripts tracker
  59. function importScriptURI(url) {
  60.     if (loadedScripts[url]) {
  61.         return null;
  62.     }
  63.     loadedScripts[url] = true;
  64.     var s = document.createElement('script');
  65.     s.setAttribute('src',url);
  66.     s.setAttribute('type','text/javascript');
  67.     document.getElementsByTagName('head')[0].appendChild(s);
  68.     return s;
  69. }
  70.  
  71. function importStylesheet(page) {
  72.     return importStylesheetURI(wgScript + '?action=raw&ctype=text/css&title=' + encodeURIComponent(page.replace(/ /g,'_')));
  73. }
  74.  
  75. function importStylesheetURI(url) {
  76.     return document.createStyleSheet ? document.createStyleSheet(url) : appendCSS('@import "' + url + '";');
  77. }
  78.  
  79. function appendCSS(text) {
  80.     var s = document.createElement('style');
  81.     s.type = 'text/css';
  82.     s.rel = 'stylesheet';
  83.     if (s.styleSheet) s.styleSheet.cssText = text //IE
  84.     else s.appendChild(document.createTextNode(text + '')) //Safari sometimes borks on null
  85.     document.getElementsByTagName('head')[0].appendChild(s);
  86.     return s;
  87. }
  88.  
  89. // special stylesheet links
  90. if (typeof stylepath != 'undefined' && typeof skin != 'undefined') {
  91.     if (is_opera_preseven) {
  92.         importStylesheetURI(stylepath+'/'+skin+'/Opera6Fixes.css');
  93.     } else if (is_opera_seven && !is_opera_95) {
  94.         importStylesheetURI(stylepath+'/'+skin+'/Opera7Fixes.css');
  95.     } else if (is_opera_95) {
  96.         importStylesheetURI(stylepath+'/'+skin+'/Opera9Fixes.css');
  97.     } else if (is_khtml) {
  98.         importStylesheetURI(stylepath+'/'+skin+'/KHTMLFixes.css');
  99.     } else if (is_ff2_) {
  100.         importStylesheetURI(stylepath+'/'+skin+'/FF2Fixes.css');
  101.     }
  102. }
  103.  
  104. if (wgBreakFrames) {
  105.     // Un-trap us from framesets
  106.     if (window.top != window) {
  107.         window.top.location = window.location;
  108.     }
  109. }
  110.  
  111. function showTocToggle() {
  112.     if (document.createTextNode) {
  113.         // Uses DOM calls to avoid document.write + XHTML issues
  114.  
  115.         var linkHolder = document.getElementById('toctitle');
  116.         if (!linkHolder) {
  117.             return;
  118.         }
  119.  
  120.         var outerSpan = document.createElement('span');
  121.         outerSpan.className = 'toctoggle';
  122.  
  123.         var toggleLink = document.createElement('a');
  124.         toggleLink.id = 'togglelink';
  125.         toggleLink.className = 'internal';
  126.         toggleLink.href = 'javascript:toggleToc()';
  127.         toggleLink.appendChild(document.createTextNode(tocHideText));
  128.  
  129.         outerSpan.appendChild(document.createTextNode('['));
  130.         outerSpan.appendChild(toggleLink);
  131.         outerSpan.appendChild(document.createTextNode(']'));
  132.  
  133.         linkHolder.appendChild(document.createTextNode(' '));
  134.         linkHolder.appendChild(outerSpan);
  135.  
  136.         var cookiePos = document.cookie.indexOf("hidetoc=");
  137.         if (cookiePos > -1 && document.cookie.charAt(cookiePos + 8) == 1) {
  138.             toggleToc();
  139.         }
  140.     }
  141. }
  142.  
  143. function changeText(el, newText) {
  144.     // Safari work around
  145.     if (el.innerText) {
  146.         el.innerText = newText;
  147.     } else if (el.firstChild && el.firstChild.nodeValue) {
  148.         el.firstChild.nodeValue = newText;
  149.     }
  150. }
  151.  
  152. function toggleToc() {
  153.     var toc = document.getElementById('toc').getElementsByTagName('ul')[0];
  154.     var toggleLink = document.getElementById('togglelink');
  155.  
  156.     if (toc && toggleLink && toc.style.display == 'none') {
  157.         changeText(toggleLink, tocHideText);
  158.         toc.style.display = 'block';
  159.         document.cookie = "hidetoc=0";
  160.     } else {
  161.         changeText(toggleLink, tocShowText);
  162.         toc.style.display = 'none';
  163.         document.cookie = "hidetoc=1";
  164.     }
  165. }
  166.  
  167. var mwEditButtons = [];
  168. var mwCustomEditButtons = []; // eg to add in MediaWiki:Common.js
  169.  
  170. function escapeQuotes(text) {
  171.     var re = new RegExp("'","g");
  172.     text = text.replace(re,"\\'");
  173.     re = new RegExp("\\n","g");
  174.     text = text.replace(re,"\\n");
  175.     return escapeQuotesHTML(text);
  176. }
  177.  
  178. function escapeQuotesHTML(text) {
  179.     var re = new RegExp('&',"g");
  180.     text = text.replace(re,"&");
  181.     re = new RegExp('"',"g");
  182.     text = text.replace(re,""");
  183.     re = new RegExp('<',"g");
  184.     text = text.replace(re,"<");
  185.     re = new RegExp('>',"g");
  186.     text = text.replace(re,">");
  187.     return text;
  188. }
  189.  
  190.  
  191. /**
  192.  * Set the accesskey prefix based on browser detection.
  193.  */
  194. var tooltipAccessKeyPrefix = 'alt-';
  195. if (is_opera) {
  196.     tooltipAccessKeyPrefix = 'shift-esc-';
  197. } else if (!is_safari_win && is_safari && webkit_version > 526) {
  198.     tooltipAccessKeyPrefix = 'ctrl-alt-';
  199. } else if (!is_safari_win && (is_safari
  200.         || clientPC.indexOf('mac') != -1
  201.         || clientPC.indexOf('konqueror') != -1 )) {
  202.     tooltipAccessKeyPrefix = 'ctrl-';
  203. } else if (is_ff2) {
  204.     tooltipAccessKeyPrefix = 'alt-shift-';
  205. }
  206. var tooltipAccessKeyRegexp = /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/;
  207.  
  208. /**
  209.  * Add the appropriate prefix to the accesskey shown in the tooltip.
  210.  * If the nodeList parameter is given, only those nodes are updated;
  211.  * otherwise, all the nodes that will probably have accesskeys by
  212.  * default are updated.
  213.  *
  214.  * @param Array nodeList -- list of elements to update
  215.  */
  216. function updateTooltipAccessKeys( nodeList ) {
  217.     if ( !nodeList ) {
  218.         // skins without a "column-one" element don't seem to have links with accesskeys either
  219.         var columnOne = document.getElementById("column-one");
  220.         if ( columnOne )
  221.             updateTooltipAccessKeys( columnOne.getElementsByTagName("a") );
  222.         // these are rare enough that no such optimization is needed
  223.         updateTooltipAccessKeys( document.getElementsByTagName("input") );
  224.         updateTooltipAccessKeys( document.getElementsByTagName("label") );
  225.         return;
  226.     }
  227.  
  228.     for ( var i = 0; i < nodeList.length; i++ ) {
  229.         var element = nodeList[i];
  230.         var tip = element.getAttribute("title");
  231.         if ( tip && tooltipAccessKeyRegexp.exec(tip) ) {
  232.             tip = tip.replace(tooltipAccessKeyRegexp,
  233.                       "["+tooltipAccessKeyPrefix+"$5]");
  234.             element.setAttribute("title", tip );
  235.         }
  236.     }
  237. }
  238.  
  239. /**
  240.  * Add a link to one of the portlet menus on the page, including:
  241.  *
  242.  * p-cactions: Content actions (shown as tabs above the main content in Monobook)
  243.  * p-personal: Personal tools (shown at the top right of the page in Monobook)
  244.  * p-navigation: Navigation
  245.  * p-tb: Toolbox
  246.  *
  247.  * This function exists for the convenience of custom JS authors.  All
  248.  * but the first three parameters are optional, though providing at
  249.  * least an id and a tooltip is recommended.
  250.  *
  251.  * By default the new link will be added to the end of the list.  To
  252.  * add the link before a given existing item, pass the DOM node of
  253.  * that item (easily obtained with document.getElementById()) as the
  254.  * nextnode parameter; to add the link _after_ an existing item, pass
  255.  * the node's nextSibling instead.
  256.  *
  257.  * @param String portlet -- id of the target portlet ("p-cactions", "p-personal", "p-navigation" or "p-tb")
  258.  * @param String href -- link URL
  259.  * @param String text -- link text (will be automatically lowercased by CSS for p-cactions in Monobook)
  260.  * @param String id -- id of the new item, should be unique and preferably have the appropriate prefix ("ca-", "pt-", "n-" or "t-")
  261.  * @param String tooltip -- text to show when hovering over the link, without accesskey suffix
  262.  * @param String accesskey -- accesskey to activate this link (one character, try to avoid conflicts)
  263.  * @param Node nextnode -- the DOM node before which the new item should be added, should be another item in the same list
  264.  *
  265.  * @return Node -- the DOM node of the new item (an LI element) or null
  266.  */
  267. function addPortletLink(portlet, href, text, id, tooltip, accesskey, nextnode) {
  268.     var node = document.getElementById(portlet);
  269.     if ( !node ) return null;
  270.     node = node.getElementsByTagName( "ul" )[0];
  271.     if ( !node ) return null;
  272.  
  273.     var link = document.createElement( "a" );
  274.     link.appendChild( document.createTextNode( text ) );
  275.     link.href = href;
  276.  
  277.     var item = document.createElement( "li" );
  278.     item.appendChild( link );
  279.     if ( id ) item.id = id;
  280.  
  281.     if ( accesskey ) {
  282.         link.setAttribute( "accesskey", accesskey );
  283.         tooltip += " ["+accesskey+"]";
  284.     }
  285.     if ( tooltip ) {
  286.         link.setAttribute( "title", tooltip );
  287.     }
  288.     if ( accesskey && tooltip ) {
  289.         updateTooltipAccessKeys( new Array( link ) );
  290.     }
  291.  
  292.     if ( nextnode && nextnode.parentNode == node )
  293.         node.insertBefore( item, nextnode );
  294.     else
  295.         node.appendChild( item );  // IE compatibility (?)
  296.  
  297.     return item;
  298. }
  299.  
  300. function getInnerText(el) {
  301.     if (typeof el == "string") return el;
  302.     if (typeof el == "undefined") { return el };
  303.     if (el.textContent) return el.textContent; // not needed but it is faster
  304.     if (el.innerText) return el.innerText;     // IE doesn't have textContent
  305.     var str = "";
  306.  
  307.     var cs = el.childNodes;
  308.     var l = cs.length;
  309.     for (var i = 0; i < l; i++) {
  310.         switch (cs[i].nodeType) {
  311.             case 1: //ELEMENT_NODE
  312.                 str += ts_getInnerText(cs[i]);
  313.                 break;
  314.             case 3:    //TEXT_NODE
  315.                 str += cs[i].nodeValue;
  316.                 break;
  317.         }
  318.     }
  319.     return str;
  320. }
  321.  
  322.  
  323. /**
  324.  * Set up accesskeys/tooltips from the deprecated ta array.  If doId
  325.  * is specified, only set up for that id.  Note that this function is
  326.  * deprecated and will not be supported indefinitely -- use
  327.  * updateTooltipAccessKey() instead.
  328.  *
  329.  * @param mixed doId string or null
  330.  */
  331. function akeytt( doId ) {
  332.     // A lot of user scripts (and some of the code below) break if
  333.     // ta isn't defined, so we make sure it is.  Explictly using
  334.     // window.ta avoids a "ta is not defined" error.
  335.     if (!window.ta) window.ta = new Array;
  336.  
  337.     // Make a local, possibly restricted, copy to avoid clobbering
  338.     // the original.
  339.     var ta;
  340.     if ( doId ) {
  341.         ta = [doId];
  342.     } else {
  343.         ta = window.ta;
  344.     }
  345.  
  346.     // Now deal with evil deprecated ta
  347.     var watchCheckboxExists = document.getElementById( 'wpWatchthis' ) ? true : false;
  348.     for (var id in ta) {
  349.         var n = document.getElementById(id);
  350.         if (n) {
  351.             var a = null;
  352.             var ak = '';
  353.             // Are we putting accesskey in it
  354.             if (ta[id][0].length > 0) {
  355.                 // Is this object a object? If not assume it's the next child.
  356.  
  357.                 if (n.nodeName.toLowerCase() == "a") {
  358.                     a = n;
  359.                 } else {
  360.                     a = n.childNodes[0];
  361.                 }
  362.                  // Don't add an accesskey for the watch tab if the watch
  363.                  // checkbox is also available.
  364.                 if (a && ((id != 'ca-watch' && id != 'ca-unwatch') || !watchCheckboxExists)) {
  365.                     a.accessKey = ta[id][0];
  366.                     ak = ' ['+tooltipAccessKeyPrefix+ta[id][0]+']';
  367.                 }
  368.             } else {
  369.                 // We don't care what type the object is when assigning tooltip
  370.                 a = n;
  371.                 ak = '';
  372.             }
  373.  
  374.             if (a) {
  375.                 a.title = ta[id][1]+ak;
  376.             }
  377.         }
  378.     }
  379. }
  380.  
  381. var checkboxes;
  382. var lastCheckbox;
  383.  
  384. function setupCheckboxShiftClick() {
  385.     checkboxes = [];
  386.     lastCheckbox = null;
  387.     var inputs = document.getElementsByTagName('input');
  388.     addCheckboxClickHandlers(inputs);
  389. }
  390.  
  391. function addCheckboxClickHandlers(inputs, start) {
  392.     if ( !start) start = 0;
  393.  
  394.     var finish = start + 250;
  395.     if ( finish > inputs.length )
  396.         finish = inputs.length;
  397.  
  398.     for ( var i = start; i < finish; i++ ) {
  399.         var cb = inputs[i];
  400.         if ( !cb.type || cb.type.toLowerCase() != 'checkbox' )
  401.             continue;
  402.         var end = checkboxes.length;
  403.         checkboxes[end] = cb;
  404.         cb.index = end;
  405.         cb.onclick = checkboxClickHandler;
  406.     }
  407.  
  408.     if ( finish < inputs.length ) {
  409.         setTimeout( function () {
  410.             addCheckboxClickHandlers(inputs, finish);
  411.         }, 200 );
  412.     }
  413. }
  414.  
  415. function checkboxClickHandler(e) {
  416.     if (typeof e == 'undefined') {
  417.         e = window.event;
  418.     }
  419.     if ( !e.shiftKey || lastCheckbox === null ) {
  420.         lastCheckbox = this.index;
  421.         return true;
  422.     }
  423.     var endState = this.checked;
  424.     var start, finish;
  425.     if ( this.index < lastCheckbox ) {
  426.         start = this.index + 1;
  427.         finish = lastCheckbox;
  428.     } else {
  429.         start = lastCheckbox;
  430.         finish = this.index - 1;
  431.     }
  432.     for (var i = start; i <= finish; ++i ) {
  433.         checkboxes[i].checked = endState;
  434.     }
  435.     lastCheckbox = this.index;
  436.     return true;
  437. }
  438.  
  439. function toggle_element_activation(ida,idb) {
  440.     if (!document.getElementById) {
  441.         return;
  442.     }
  443.     document.getElementById(ida).disabled=true;
  444.     document.getElementById(idb).disabled=false;
  445. }
  446.  
  447. function toggle_element_check(ida,idb) {
  448.     if (!document.getElementById) {
  449.         return;
  450.     }
  451.     document.getElementById(ida).checked=true;
  452.     document.getElementById(idb).checked=false;
  453. }
  454.  
  455. /*
  456.     Written by Jonathan Snook, http://www.snook.ca/jonathan
  457.     Add-ons by Robert Nyman, http://www.robertnyman.com
  458.     Author says "The credit comment is all it takes, no license. Go crazy with it!:-)"
  459.     From http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
  460. */
  461. function getElementsByClassName(oElm, strTagName, oClassNames){
  462.     var arrReturnElements = new Array();
  463.     if ( typeof( oElm.getElementsByClassName ) == "function" ) {
  464.         /* Use a native implementation where possible FF3, Saf3.2, Opera 9.5 */
  465.         var arrNativeReturn = oElm.getElementsByClassName( oClassNames );
  466.         if ( strTagName == "*" )
  467.             return arrNativeReturn;
  468.         for ( var h=0; h < arrNativeReturn.length; h++ ) {
  469.             if( arrNativeReturn[h].tagName.toLowerCase() == strTagName.toLowerCase() )
  470.                 arrReturnElements[arrReturnElements.length] = arrNativeReturn[h];
  471.         }
  472.         return arrReturnElements;
  473.     }
  474.     var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
  475.     var arrRegExpClassNames = new Array();
  476.     if(typeof oClassNames == "object"){
  477.         for(var i=0; i<oClassNames.length; i++){
  478.             arrRegExpClassNames[arrRegExpClassNames.length] =
  479.                 new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)");
  480.         }
  481.     }
  482.     else{
  483.         arrRegExpClassNames[arrRegExpClassNames.length] =
  484.             new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)");
  485.     }
  486.     var oElement;
  487.     var bMatchesAll;
  488.     for(var j=0; j<arrElements.length; j++){
  489.         oElement = arrElements[j];
  490.         bMatchesAll = true;
  491.         for(var k=0; k<arrRegExpClassNames.length; k++){
  492.             if(!arrRegExpClassNames[k].test(oElement.className)){
  493.                 bMatchesAll = false;
  494.                 break;
  495.             }
  496.         }
  497.         if(bMatchesAll){
  498.             arrReturnElements[arrReturnElements.length] = oElement;
  499.         }
  500.     }
  501.     return (arrReturnElements)
  502. }
  503.  
  504. function redirectToFragment(fragment) {
  505.     var match = navigator.userAgent.match(/AppleWebKit\/(\d+)/);
  506.     if (match) {
  507.         var webKitVersion = parseInt(match[1]);
  508.         if (webKitVersion < 420) {
  509.             // Released Safari w/ WebKit 418.9.1 messes up horribly
  510.             // Nightlies of 420+ are ok
  511.             return;
  512.         }
  513.     }
  514.     if (is_gecko) {
  515.         // Mozilla needs to wait until after load, otherwise the window doesn't scroll
  516.         addOnloadHook(function () {
  517.             if (window.location.hash == "")
  518.                 window.location.hash = fragment;
  519.         });
  520.     } else {
  521.         if (window.location.hash == "")
  522.             window.location.hash = fragment;
  523.     }
  524. }
  525.  
  526. /*
  527.  * Table sorting script based on one (c) 1997-2006 Stuart Langridge and Joost
  528.  * de Valk:
  529.  * http://www.joostdevalk.nl/code/sortable-table/
  530.  * http://www.kryogenix.org/code/browser/sorttable/
  531.  *
  532.  * @todo don't break on colspans/rowspans (bug 8028)
  533.  * @todo language-specific digit grouping/decimals (bug 8063)
  534.  * @todo support all accepted date formats (bug 8226)
  535.  */
  536.  
  537. var ts_image_path = stylepath+"/common/images/";
  538. var ts_image_up = "sort_up.gif";
  539. var ts_image_down = "sort_down.gif";
  540. var ts_image_none = "sort_none.gif";
  541. var ts_europeandate = wgContentLanguage != "en"; // The non-American-inclined can change to "true"
  542. var ts_alternate_row_colors = false;
  543. var ts_number_transform_table = null;
  544. var ts_number_regex = null;
  545.  
  546. function sortables_init() {
  547.     var idnum = 0;
  548.     // Find all tables with class sortable and make them sortable
  549.     var tables = getElementsByClassName(document, "table", "sortable");
  550.     for (var ti = 0; ti < tables.length ; ti++) {
  551.         if (!tables[ti].id) {
  552.             tables[ti].setAttribute('id','sortable_table_id_'+idnum);
  553.             ++idnum;
  554.         }
  555.         ts_makeSortable(tables[ti]);
  556.     }
  557. }
  558.  
  559. function ts_makeSortable(table) {
  560.     var firstRow;
  561.     if (table.rows && table.rows.length > 0) {
  562.         if (table.tHead && table.tHead.rows.length > 0) {
  563.             firstRow = table.tHead.rows[table.tHead.rows.length-1];
  564.         } else {
  565.             firstRow = table.rows[0];
  566.         }
  567.     }
  568.     if (!firstRow) return;
  569.  
  570.     // We have a first row: assume it's the header, and make its contents clickable links
  571.     for (var i = 0; i < firstRow.cells.length; i++) {
  572.         var cell = firstRow.cells[i];
  573.         if ((" "+cell.className+" ").indexOf(" unsortable ") == -1) {
  574.             cell.innerHTML += '  '
  575.                 + '<a href="#" class="sortheader" '
  576.                 + 'onclick="ts_resortTable(this);return false;">'
  577.                 + '<span class="sortarrow">'
  578.                 + '<img src="'
  579.                 + ts_image_path
  580.                 + ts_image_none
  581.                 + '" alt="↓"/></span></a>';
  582.         }
  583.     }
  584.     if (ts_alternate_row_colors) {
  585.         ts_alternate(table);
  586.     }
  587. }
  588.  
  589. function ts_getInnerText(el) {
  590.     return getInnerText( el );
  591. }
  592.  
  593. function ts_resortTable(lnk) {
  594.     // get the span
  595.     var span = lnk.getElementsByTagName('span')[0];
  596.  
  597.     var td = lnk.parentNode;
  598.     var tr = td.parentNode;
  599.     var column = td.cellIndex;
  600.  
  601.     var table = tr.parentNode;
  602.     while (table && !(table.tagName && table.tagName.toLowerCase() == 'table'))
  603.         table = table.parentNode;
  604.     if (!table) return;
  605.  
  606.     if (table.rows.length <= 1) return;
  607.  
  608.     // Generate the number transform table if it's not done already
  609.     if (ts_number_transform_table == null) {
  610.         ts_initTransformTable();
  611.     }
  612.  
  613.     // Work out a type for the column
  614.     // Skip the first row if that's where the headings are
  615.     var rowStart = (table.tHead && table.tHead.rows.length > 0 ? 0 : 1);
  616.  
  617.     var itm = "";
  618.     for (var i = rowStart; i < table.rows.length; i++) {
  619.         if (table.rows[i].cells.length > column) {
  620.             itm = ts_getInnerText(table.rows[i].cells[column]);
  621.             itm = itm.replace(/^[\s\xa0]+/, "").replace(/[\s\xa0]+$/, "");
  622.             if (itm != "") break;
  623.         }
  624.     }
  625.  
  626.     // TODO: bug 8226, localised date formats
  627.     var sortfn = ts_sort_generic;
  628.     var preprocessor = ts_toLowerCase;
  629.     if (/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/.test(itm)) {
  630.         preprocessor = ts_dateToSortKey;
  631.     } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/.test(itm)) {
  632.         preprocessor = ts_dateToSortKey;
  633.     } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d$/.test(itm)) {
  634.         preprocessor = ts_dateToSortKey;
  635.     // pound dollar euro yen currency cents
  636.     } else if (/(^[\u00a3$\u20ac\u00a4\u00a5]|\u00a2$)/.test(itm)) {
  637.         preprocessor = ts_currencyToSortKey;
  638.     } else if (ts_number_regex.test(itm)) {
  639.         preprocessor = ts_parseFloat;
  640.     }
  641.  
  642.     var reverse = (span.getAttribute("sortdir") == 'down');
  643.  
  644.     var newRows = new Array();
  645.     var staticRows = new Array();
  646.     for (var j = rowStart; j < table.rows.length; j++) {
  647.         var row = table.rows[j];
  648.         if((" "+row.className+" ").indexOf(" unsortable ") < 0) {
  649.             var keyText = ts_getInnerText(row.cells[column]);
  650.             var oldIndex = (reverse ? -j : j);
  651.             var preprocessed = preprocessor( keyText );
  652.  
  653.             newRows[newRows.length] = new Array(row, preprocessed, oldIndex);
  654.         } else staticRows[staticRows.length] = new Array(row, false, j-rowStart);
  655.     }
  656.  
  657.     newRows.sort(sortfn);
  658.  
  659.     var arrowHTML;
  660.     if (reverse) {
  661.         arrowHTML = '<img src="'+ ts_image_path + ts_image_down + '" alt="↓"/>';
  662.         newRows.reverse();
  663.         span.setAttribute('sortdir','up');
  664.     } else {
  665.         arrowHTML = '<img src="'+ ts_image_path + ts_image_up + '" alt="↑"/>';
  666.         span.setAttribute('sortdir','down');
  667.     }
  668.  
  669.     for (var i = 0; i < staticRows.length; i++) {
  670.         var row = staticRows[i];
  671.         newRows.splice(row[2], 0, row);
  672.     }
  673.  
  674.     // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
  675.     // don't do sortbottom rows
  676.     for (var i = 0; i < newRows.length; i++) {
  677.         if ((" "+newRows[i][0].className+" ").indexOf(" sortbottom ") == -1)
  678.             table.tBodies[0].appendChild(newRows[i][0]);
  679.     }
  680.     // do sortbottom rows only
  681.     for (var i = 0; i < newRows.length; i++) {
  682.         if ((" "+newRows[i][0].className+" ").indexOf(" sortbottom ") != -1)
  683.             table.tBodies[0].appendChild(newRows[i][0]);
  684.     }
  685.  
  686.     // Delete any other arrows there may be showing
  687.     var spans = getElementsByClassName(tr, "span", "sortarrow");
  688.     for (var i = 0; i < spans.length; i++) {
  689.         spans[i].innerHTML = '<img src="'+ ts_image_path + ts_image_none + '" alt="↓"/>';
  690.     }
  691.     span.innerHTML = arrowHTML;
  692.  
  693.     if (ts_alternate_row_colors) {
  694.         ts_alternate(table);
  695.     }
  696. }
  697.  
  698. function ts_initTransformTable() {
  699.     if ( typeof wgSeparatorTransformTable == "undefined"
  700.             || ( wgSeparatorTransformTable[0] == '' && wgDigitTransformTable[2] == '' ) )
  701.     {
  702.         digitClass = "[0-9,.]";
  703.         ts_number_transform_table = false;
  704.     } else {
  705.         ts_number_transform_table = {};
  706.         // Unpack the transform table
  707.         // Separators
  708.         ascii = wgSeparatorTransformTable[0].split("\t");
  709.         localised = wgSeparatorTransformTable[1].split("\t");
  710.         for ( var i = 0; i < ascii.length; i++ ) { 
  711.             ts_number_transform_table[localised[i]] = ascii[i];
  712.         }
  713.         // Digits
  714.         ascii = wgDigitTransformTable[0].split("\t");
  715.         localised = wgDigitTransformTable[1].split("\t");
  716.         for ( var i = 0; i < ascii.length; i++ ) { 
  717.             ts_number_transform_table[localised[i]] = ascii[i];
  718.         }
  719.  
  720.         // Construct regex for number identification
  721.         digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '\\.'];
  722.         maxDigitLength = 1;
  723.         for ( var digit in ts_number_transform_table ) {
  724.             // Escape regex metacharacters
  725.             digits.push( 
  726.                 digit.replace( /[\\\\$\*\+\?\.\(\)\|\{\}\[\]\-]/,
  727.                     function( s ) { return '\\' + s; } )
  728.             );
  729.             if (digit.length > maxDigitLength) {
  730.                 maxDigitLength = digit.length;
  731.             }
  732.         }
  733.         if ( maxDigitLength > 1 ) {
  734.             digitClass = '[' + digits.join( '', digits ) + ']';
  735.         } else {
  736.             digitClass = '(' + digits.join( '|', digits ) + ')';
  737.         }
  738.     }
  739.  
  740.     // We allow a trailing percent sign, which we just strip.  This works fine
  741.     // if percents and regular numbers aren't being mixed.
  742.     ts_number_regex = new RegExp(
  743.         "^(" +
  744.             "[+-]?[0-9][0-9,]*(\\.[0-9,]*)?(E[+-]?[0-9][0-9,]*)?" + // Fortran-style scientific
  745.             "|" +
  746.             "[+-]?" + digitClass + "+%?" + // Generic localised
  747.         ")$", "i"
  748.     );
  749. }
  750.  
  751. function ts_toLowerCase( s ) {
  752.     return s.toLowerCase();
  753. }
  754.  
  755. function ts_dateToSortKey(date) {    
  756.     // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
  757.     if (date.length == 11) {
  758.         switch (date.substr(3,3).toLowerCase()) {
  759.             case "jan": var month = "01"; break;
  760.             case "feb": var month = "02"; break;
  761.             case "mar": var month = "03"; break;
  762.             case "apr": var month = "04"; break;
  763.             case "may": var month = "05"; break;
  764.             case "jun": var month = "06"; break;
  765.             case "jul": var month = "07"; break;
  766.             case "aug": var month = "08"; break;
  767.             case "sep": var month = "09"; break;
  768.             case "oct": var month = "10"; break;
  769.             case "nov": var month = "11"; break;
  770.             case "dec": var month = "12"; break;
  771.             // default: var month = "00";
  772.         }
  773.         return date.substr(7,4)+month+date.substr(0,2);
  774.     } else if (date.length == 10) {
  775.         if (ts_europeandate == false) {
  776.             return date.substr(6,4)+date.substr(0,2)+date.substr(3,2);
  777.         } else {
  778.             return date.substr(6,4)+date.substr(3,2)+date.substr(0,2);
  779.         }
  780.     } else if (date.length == 8) {
  781.         yr = date.substr(6,2);
  782.         if (parseInt(yr) < 50) { 
  783.             yr = '20'+yr; 
  784.         } else { 
  785.             yr = '19'+yr; 
  786.         }
  787.         if (ts_europeandate == true) {
  788.             return yr+date.substr(3,2)+date.substr(0,2);
  789.         } else {
  790.             return yr+date.substr(0,2)+date.substr(3,2);
  791.         }
  792.     }
  793.     return "00000000";
  794. }
  795.  
  796. function ts_parseFloat( s ) {
  797.     if ( !s ) {
  798.         return 0;
  799.     }
  800.     if (ts_number_transform_table != false) {
  801.         var newNum = '', c;
  802.         
  803.         for ( var p = 0; p < s.length; p++ ) {
  804.             c = s.charAt( p );
  805.             if (c in ts_number_transform_table) {
  806.                 newNum += ts_number_transform_table[c];
  807.             } else {
  808.                 newNum += c;
  809.             }
  810.         }
  811.         s = newNum;
  812.     }
  813.  
  814.     num = parseFloat(s.replace(/,/g, ""));
  815.     return (isNaN(num) ? 0 : num);
  816. }
  817.  
  818. function ts_currencyToSortKey( s ) {
  819.     return ts_parseFloat(s.replace(/[^0-9.,]/g,''));
  820. }
  821.  
  822. function ts_sort_generic(a, b) {
  823.     return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2];
  824. }
  825.  
  826. function ts_alternate(table) {
  827.     // Take object table and get all it's tbodies.
  828.     var tableBodies = table.getElementsByTagName("tbody");
  829.     // Loop through these tbodies
  830.     for (var i = 0; i < tableBodies.length; i++) {
  831.         // Take the tbody, and get all it's rows
  832.         var tableRows = tableBodies[i].getElementsByTagName("tr");
  833.         // Loop through these rows
  834.         // Start at 1 because we want to leave the heading row untouched
  835.         for (var j = 0; j < tableRows.length; j++) {
  836.             // Check if j is even, and apply classes for both possible results
  837.             var oldClasses = tableRows[j].className.split(" ");
  838.             var newClassName = "";
  839.             for (var k = 0; k < oldClasses.length; k++) {
  840.                 if (oldClasses[k] != "" && oldClasses[k] != "even" && oldClasses[k] != "odd")
  841.                     newClassName += oldClasses[k] + " ";
  842.             }
  843.             tableRows[j].className = newClassName + (j % 2 == 0 ? "even" : "odd");
  844.         }
  845.     }
  846. }
  847.  
  848. /*
  849.  * End of table sorting code
  850.  */
  851.  
  852.  
  853. /**
  854.  * Add a cute little box at the top of the screen to inform the user of
  855.  * something, replacing any preexisting message.
  856.  *
  857.  * @param String -or- Dom Object message HTML to be put inside the right div
  858.  * @param String className   Used in adding a class; should be different for each
  859.  *   call to allow CSS/JS to hide different boxes.  null = no class used.
  860.  * @return Boolean       True on success, false on failure
  861.  */
  862. function jsMsg( message, className ) {
  863.     if ( !document.getElementById ) {
  864.         return false;
  865.     }
  866.     // We special-case skin structures provided by the software.  Skins that
  867.     // choose to abandon or significantly modify our formatting can just define
  868.     // an mw-js-message div to start with.
  869.     var messageDiv = document.getElementById( 'mw-js-message' );
  870.     if ( !messageDiv ) {
  871.         messageDiv = document.createElement( 'div' );
  872.         if ( document.getElementById( 'column-content' )
  873.         && document.getElementById( 'content' ) ) {
  874.             // MonoBook, presumably
  875.             document.getElementById( 'content' ).insertBefore(
  876.                 messageDiv,
  877.                 document.getElementById( 'content' ).firstChild
  878.             );
  879.         } else if ( document.getElementById('content')
  880.         && document.getElementById( 'article' ) ) {
  881.             // Non-Monobook but still recognizable (old-style)
  882.             document.getElementById( 'article').insertBefore(
  883.                 messageDiv,
  884.                 document.getElementById( 'article' ).firstChild
  885.             );
  886.         } else {
  887.             return false;
  888.         }
  889.     }
  890.  
  891.     messageDiv.setAttribute( 'id', 'mw-js-message' );
  892.     messageDiv.style.display = 'block';
  893.     if( className ) {
  894.         messageDiv.setAttribute( 'class', 'mw-js-message-'+className );
  895.     }
  896.     
  897.     if (typeof message === 'object') {
  898.         while (messageDiv.hasChildNodes()) // Remove old content
  899.             messageDiv.removeChild(messageDiv.firstChild);
  900.         messageDiv.appendChild (message); // Append new content
  901.     }
  902.     else {
  903.         messageDiv.innerHTML = message;
  904.     }
  905.     return true;
  906. }
  907.  
  908. /**
  909.  * Inject a cute little progress spinner after the specified element
  910.  *
  911.  * @param element Element to inject after
  912.  * @param id Identifier string (for use with removeSpinner(), below)
  913.  */
  914. function injectSpinner( element, id ) {
  915.     var spinner = document.createElement( "img" );
  916.     spinner.id = "mw-spinner-" + id;
  917.     spinner.src = stylepath + "/common/images/spinner.gif";
  918.     spinner.alt = spinner.title = "...";
  919.     if( element.nextSibling ) {
  920.         element.parentNode.insertBefore( spinner, element.nextSibling );
  921.     } else {
  922.         element.parentNode.appendChild( spinner );
  923.     }
  924. }
  925.  
  926. /**
  927.  * Remove a progress spinner added with injectSpinner()
  928.  *
  929.  * @param id Identifier string
  930.  */
  931. function removeSpinner( id ) {
  932.     var spinner = document.getElementById( "mw-spinner-" + id );
  933.     if( spinner ) {
  934.         spinner.parentNode.removeChild( spinner );
  935.     }
  936. }
  937.  
  938. function runOnloadHook() {
  939.     // don't run anything below this for non-dom browsers
  940.     if (doneOnloadHook || !(document.getElementById && document.getElementsByTagName)) {
  941.         return;
  942.     }
  943.  
  944.     // set this before running any hooks, since any errors below
  945.     // might cause the function to terminate prematurely
  946.     doneOnloadHook = true;
  947.  
  948.     updateTooltipAccessKeys( null );
  949.     akeytt( null );
  950.     setupCheckboxShiftClick();
  951.     sortables_init();
  952.  
  953.     // Run any added-on functions
  954.     for (var i = 0; i < onloadFuncts.length; i++) {
  955.         onloadFuncts[i]();
  956.     }
  957. }
  958.  
  959. /**
  960.  * Add an event handler to an element
  961.  *
  962.  * @param Element element Element to add handler to
  963.  * @param String attach Event to attach to
  964.  * @param callable handler Event handler callback
  965.  */
  966. function addHandler( element, attach, handler ) {
  967.     if( window.addEventListener ) {
  968.         element.addEventListener( attach, handler, false );
  969.     } else if( window.attachEvent ) {
  970.         element.attachEvent( 'on' + attach, handler );
  971.     }
  972. }
  973.  
  974. /**
  975.  * Add a click event handler to an element
  976.  *
  977.  * @param Element element Element to add handler to
  978.  * @param callable handler Event handler callback
  979.  */
  980. function addClickHandler( element, handler ) {
  981.     addHandler( element, 'click', handler );
  982. }
  983.  
  984. /**
  985.  * Removes an event handler from an element
  986.  *
  987.  * @param Element element Element to remove handler from
  988.  * @param String remove Event to remove
  989.  * @param callable handler Event handler callback to remove
  990.  */
  991. function removeHandler( element, remove, handler ) {
  992.     if( window.removeEventListener ) {
  993.         element.removeEventListener( remove, handler, false );
  994.     } else if( window.detachEvent ) {
  995.         element.detachEvent( 'on' + remove, handler );
  996.     }
  997. }
  998. //note: all skins should call runOnloadHook() at the end of html output,
  999. //      so the below should be redundant. It's there just in case.
  1000. hookEvent("load", runOnloadHook);
  1001.